package com.example.demo.utils.excelUtil;

import jxl.Cell;
import jxl.Sheet;
import jxl.Workbook;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author yongsheng.he
 * @describe
 * @date 2017/10/19 16:17
 */
public class ExcelUtilTest {
	
	/**
	 * @param in           ：承载着Excel的输入流
	 * @param entityClass  ：List中对象的类型（Excel中的每一行都要转化为该类型的对象）
	 * @param fieldMap     ：Excel中的中文列头和类的英文属性的对应关系Map
	 * @param uniqueFields ：指定业务主键组合（即复合主键），这些列的组合不能重复
	 *
	 * @return ：List
	 *
	 * @throws ExcelException
	 * @MethodName : excelToList
	 * @Description : 将Excel转化为List
	 */
	public static <T> List<T> excelToList(InputStream in, String sheetName, Class<T> entityClass, Map<String, String>
            fieldMap) throws ExcelException {
		
		//定义要返回的list
		List<T> resultList = new ArrayList<T>();
		
		try {
			//根据Excel数据源创建WorkBook
			Workbook wb = Workbook.getWorkbook(in);
			//获取工作表
			Sheet sheet = wb.getSheet(sheetName);
			
			//获取工作表的有效行数
			int realRows = 0;
			for (int i = 0; i < sheet.getRows(); i++) {
				
				int nullCols = 0;
				for (int j = 0; j < sheet.getColumns(); j++) {
					Cell currentCell = sheet.getCell(j, i);
					if (currentCell == null || "".equals(currentCell.getContents().toString())) {
						nullCols++;
					}
				}
				
				if (nullCols == sheet.getColumns()) {
					break;
				} else {
					realRows++;
				}
			}
			//如果Excel中没有数据则提示错误
			if (realRows <= 1) {
				throw new ExcelException("Excel文件中没有任何数据");
			}
			
			Cell[] firstRow = sheet.getRow(0);
			
			String[] excelFieldNames = new String[firstRow.length];
			
			//获取Excel中的列名
			for (int i = 0; i < firstRow.length; i++) {
				excelFieldNames[i] = firstRow[i].getContents().toString().trim();
			}
			
			//判断需要的字段在Excel中是否都存在
			boolean isExist = true;
			List<String> excelFieldList = Arrays.asList(excelFieldNames);
			for (String cnName : fieldMap.keySet()) {
				if (! excelFieldList.contains(cnName)) {
					isExist = false;
					break;
				}
			}
			
			//如果有列名不存在，则抛出异常，提示错误
			if (! isExist) {
				throw new ExcelException("Excel中缺少必要的字段，或字段名称有误");
			}
			
			//将列名和列号放入Map中,这样通过列名就可以拿到列号
			LinkedHashMap<String, Integer> colMap = new LinkedHashMap<String, Integer>();
			for (int i = 0; i < excelFieldNames.length; i++) {
				colMap.put(excelFieldNames[i], firstRow[i].getColumn());
			}
			
			
			//将sheet转换为list
			for (int i = 1; i < realRows; i++) {
				//新建要转换的对象
				T entity = entityClass.newInstance();
				
				//给对象中的字段赋值
				for (Map.Entry<String, String> entry : fieldMap.entrySet()) {
					//获取中文字段名
					String cnNormalName = entry.getKey();
					//获取英文字段名
					String enNormalName = entry.getValue();
					//根据中文字段名获取列号
					int col = colMap.get(cnNormalName);
					//获取当前单元格中的内容
					String content = sheet.getCell(col, i).getContents().toString().trim();
					
					//给对象赋值
					setFieldValueByName(enNormalName, content, entity);
				}
				
				resultList.add(entity);
			}
		} catch (Exception e) {
			e.printStackTrace();
			//如果是ExcelException，则直接抛出
			if (e instanceof ExcelException) {
				throw (ExcelException) e;
				//否则将其它异常包装成ExcelException再抛出
			} else {
				e.printStackTrace();
				throw new ExcelException("导入Excel失败");
			}
		}
		return resultList;
	}
	
	/*<-------------------------辅助的私有方法----------------------------------------------->*/
	
	/**
	 * @param fieldName 字段名
	 * @param o         对象
	 *
	 * @return 字段值
	 *
	 * @MethodName : getFieldValueByName
	 * @Description : 根据字段名获取字段值
	 */
	private static Object getFieldValueByName(String fieldName, Object o) throws Exception {
		
		Object value = null;
		Field field = getFieldByName(fieldName, o.getClass());
		
		if (field != null) {
			field.setAccessible(true);
			value = field.get(o);
		} else {
			throw new ExcelException(o.getClass().getSimpleName() + "类不存在字段名 " + fieldName);
		}
		
		return value;
	}
	
	/**
	 * @param fieldName 字段名
	 * @param clazz     包含该字段的类
	 *
	 * @return 字段
	 *
	 * @MethodName : getFieldByName
	 * @Description : 根据字段名获取字段
	 */
	private static Field getFieldByName(String fieldName, Class<?> clazz) {
		//拿到本类的所有字段
		Field[] selfFields = clazz.getDeclaredFields();
		
		//如果本类中存在该字段，则返回
		for (Field field : selfFields) {
			if (field.getName().equals(fieldName)) {
				return field;
			}
		}
		
		//否则，查看父类中是否存在此字段，如果有则返回
		Class<?> superClazz = clazz.getSuperclass();
		if (superClazz != null && superClazz != Object.class) {
			return getFieldByName(fieldName, superClazz);
		}
		
		//如果本类和父类都没有，则返回空
		return null;
	}
	
	/**
	 * @param fieldNameSequence 带路径的属性名或简单属性名
	 * @param o                 对象
	 *
	 * @return 属性值
	 *
	 * @throws Exception
	 * @MethodName : getFieldValueByNameSequence
	 * @Description :
	 * 根据带路径或不带路径的属性名获取属性值
	 * 即接受简单属性名，如userName等，又接受带路径的属性名，如student.department.name等
	 */
	private static Object getFieldValueByNameSequence(String fieldNameSequence, Object o) throws Exception {
		
		Object value = null;
		
		//将fieldNameSequence进行拆分
		String[] attributes = fieldNameSequence.split("\\.");
		if (attributes.length == 1) {
			value = getFieldValueByName(fieldNameSequence, o);
		} else {
			//根据属性名获取属性对象
			Object fieldObj = getFieldValueByName(attributes[0], o);
			String subFieldNameSequence = fieldNameSequence.substring(fieldNameSequence.indexOf(".") + 1);
			value = getFieldValueByNameSequence(subFieldNameSequence, fieldObj);
		}
		return value;
		
	}
	
	/**
	 * @param fieldName  字段名
	 * @param fieldValue 字段值
	 * @param o          对象
	 *
	 * @MethodName : setFieldValueByName
	 * @Description : 根据字段名给对象的字段赋值
	 */
	private static void setFieldValueByName(String fieldName, Object fieldValue, Object o) throws Exception {
		
		Field field = getFieldByName(fieldName, o.getClass());
		if (field != null) {
			field.setAccessible(true);
			//获取字段类型
			Class<?> fieldType = field.getType();
			
			//根据字段类型给字段赋值
			if (String.class == fieldType) {
				field.set(o, String.valueOf(fieldValue));
			} else if ((Integer.TYPE == fieldType) || (Integer.class == fieldType)) {
				field.set(o, Integer.parseInt(fieldValue.toString()));
			} else if ((Long.TYPE == fieldType) || (Long.class == fieldType)) {
				field.set(o, Long.valueOf(fieldValue.toString()));
			} else if ((Float.TYPE == fieldType) || (Float.class == fieldType)) {
				field.set(o, Float.valueOf(fieldValue.toString()));
			} else if ((Short.TYPE == fieldType) || (Short.class == fieldType)) {
				field.set(o, Short.valueOf(fieldValue.toString()));
			} else if ((Double.TYPE == fieldType) || (Double.class == fieldType)) {
				field.set(o, Double.valueOf(fieldValue.toString()));
			} else if (Character.TYPE == fieldType) {
				if ((fieldValue != null) && (fieldValue.toString().length() > 0)) {
					field.set(o, Character.valueOf(fieldValue.toString().charAt(0)));
				}
			} else if (Date.class == fieldType) {
				field.set(o, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(fieldValue.toString()));
			} else {
				field.set(o, fieldValue);
			}
		} else {
			throw new ExcelException(o.getClass().getSimpleName() + "类不存在字段名 " + fieldName);
		}
	}
}
